← snapshot
2544 bytes
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { ApiError, api } from "@/lib/api";
import { isValidRefName } from "@/lib/validate";
export function BranchForm({ repo, refNames }: { repo: string; refNames: string[] }) {
const router = useRouter();
const [name, setName] = useState("");
const [from, setFrom] = useState(refNames[0] ?? "");
const [error, setError] = useState<string | null>(null);
const [pending, setPending] = useState(false);
const touched = name.length > 0;
const valid = isValidRefName(name) && from !== "";
async function submit() {
setPending(true);
setError(null);
try {
await api.branch(repo, name, from);
setName("");
router.refresh();
} catch (e) {
setError(e instanceof ApiError ? e.message : String(e));
} finally {
setPending(false);
}
}
return (
<div>
<div className="jac-cols" style={{ marginTop: 14, alignItems: "flex-end" }}>
<div className="jac-field" style={{ flex: "1 1 220px", marginTop: 0 }}>
<label className="jac-label" htmlFor="branch-name">
New ref
</label>
<input
id="branch-name"
className="jac-input"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="feature/retry"
aria-invalid={touched && !isValidRefName(name)}
/>
</div>
<div className="jac-field" style={{ flex: "1 1 160px", marginTop: 0 }}>
<label className="jac-label" htmlFor="branch-from">
From the head of
</label>
<select
id="branch-from"
className="jac-select"
value={from}
onChange={(e) => setFrom(e.target.value)}
>
{refNames.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</select>
</div>
<button
type="button"
className="jac-btn"
disabled={!valid || pending}
onClick={submit}
>
{pending ? "Branching…" : "Branch"}
</button>
</div>
{touched && !isValidRefName(name) ? (
<p className="jac-hint jac-hint--error">
Ref names: non-empty segments joined by /, no leading or trailing /, no
. or .. segments.
</p>
) : null}
{error ? <div className="jac-error-box">{error}</div> : null}
</div>
);
}